/*
 * serial.cpp
 *
 *  Created on: 2017年6月15日
 *      Author: work
 */

#include <dm/os/io/serial.hpp>

#include <dm/os/log/logger.hpp>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>

namespace dm{
namespace os{
namespace io{

static const char* logModule = "CSerial.io.os.dm";

CSerial::type_t CSerial::type()const{
	return com::CAddress::SerialPort;
}

bool CSerial::open_in( const com::CAddress& address ){
	const com::CSerialAddr* sa = address.asSerialPort();

	if( sa ){
		m_fd = ::open(sa->getDevice(),O_RDWR|O_NOCTTY);
		if( m_fd<0 ){
			log().warnning(THISMODULE "打开串口设备%s失败",sa->getDevice());
			return false;
		}

		struct termios opt;
		if( -1==tcgetattr(m_fd,&opt) ){
			log().warnning(THISMODULE "获取串行设备%s属性失败",sa->getDevice());
			close();
			return false;
		}

		cfmakeraw(&opt);

		opt.c_cflag |= (CLOCAL | CREAD);
		opt.c_cflag &= ~CRTSCTS;	// 不使用硬件流控制
		opt.c_iflag &= ~( IXON | IXOFF | IXANY );	// 不使用软件流控制

		opt.c_lflag &= ~( ICANON | ECHO | ECHOE | ISIG ); // 设置成原始输入
		opt.c_oflag &= ~OPOST;	// 设置成原始输出

		int bps = B0;
		switch( sa->getBps() ){
		case 50:
			bps = B50;
			break;
		case 75:
			bps = B75;
			break;
		case 110:
			bps = B110;
			break;
		case 134:
			bps = B134;
			break;
		case 150:
			bps = B150;
			break;
		case 200:
			bps = B200;
			break;
		case 300:
			bps = B300;
			break;
		case 600:
			bps = B600;
			break;
		case 1200:
			bps = B1200;
			break;
		case 1800:
			bps = B1800;
			break;
		case 2400:
			bps = B2400;
			break;
		case 4800:
			bps = B4800;
			break;
		case 9600:
			bps = B9600;
			break;
		case 19200:
			bps = B19200;
			break;
		case 38400:
			bps = B38400;
			break;
		case 57600:
			bps = B57600;
			break;
		case 115200:
			bps = B115200;
			break;
		case 230400:
			bps = B230400;
			break;
		default:
			return false;
		}
		cfsetispeed(&opt,bps);
		cfsetospeed(&opt,bps);

		opt.c_cflag &= ~CSIZE;
		switch(sa->getCharBits()){
		case 7:
			opt.c_cflag |= CS7;
			break;
		default:
			opt.c_cflag |= CS8;
			break;
		}

		switch( sa->getParity() ){
		case com::CSerialAddr::Odd:
			opt.c_cflag |= (PARODD|PARENB);
			opt.c_iflag |= ( INPCK | ISTRIP );
			break;
		case com::CSerialAddr::Even:
			opt.c_cflag |= PARENB;
			opt.c_cflag &= ~PARODD;
			opt.c_iflag |= ( INPCK | ISTRIP );
			break;
		default:
			opt.c_cflag &= ~PARENB;
			opt.c_iflag &= ~INPCK;
			break;
		}

		if( sa->getStop() )
			opt.c_cflag |= CSTOPB;
		else
			opt.c_cflag &= ~CSTOPB;

		if( 0!=tcsetattr(m_fd,TCSANOW,&opt) ){
			log().warnning(THISMODULE "设置串口%s属性失败",sa->getDevice());
			close();
			return false;
		}

		return true;
	}else{
		log().debug(THISMODULE "未设置地址");
		return false;
	}
}

}
}
}
